package com.wg.net.server.http;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wg.core.utils.IpUtils;
import com.wg.core.utils.NettyUtils;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.ReferenceCountUtil;
import io.netty.channel.ChannelHandler;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import static io.netty.handler.codec.http.HttpHeaderValues.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;

@ChannelHandler.Sharable
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static final Log log = LogFactory.get(HttpServerHandler.class);
    /**
     * CORS配置
     */
    private final CorsConfig corsConfig;

    public HttpServerHandler() {
        this.corsConfig = new CorsConfig();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.debug("HTTP客户端连接激活 | 通道: {}", ctx.channel());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.debug("HTTP客户端连接断开 | 通道: {}", ctx.channel());
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        super.userEventTriggered(ctx, evt);
        // 可扩展处理自定义事件（如空闲连接检测）
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("通道异常 | 通道: {}", ctx.channel(), cause);
        if (cause instanceof IOException) {
            // 忽略客户端主动断开的异常
            ctx.close();
        } else {
            // 其他异常返回500错误
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, INTERNAL_SERVER_ERROR,
                    Unpooled.copiedBuffer("Internal Server Error", StandardCharsets.UTF_8));
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=utf-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fhr) {
        final String requestId = ctx.channel().id().asShortText();
        final String ip = NettyUtils.analyzeIp(fhr, ctx);
        final String uri = fhr.uri();
        final HttpMethod method = fhr.method();
        QueryStringDecoder decoder = new QueryStringDecoder(uri);

        log.info("开始处理请求 | ID: {}, URI: {}, 方法: {}, 客户端IP: {}", requestId, uri, method, ip);

        HttpServletRequest request = new HttpServletRequest(uri, fhr.method(), ip, requestId);
        HttpServletResponse response = new HttpServletResponse(ctx, HttpUtil.isKeepAlive(fhr), uri, corsConfig);

        try {
            // 处理跨域预检请求（OPTIONS）
            if (method == HttpMethod.OPTIONS) {
                handleCorsPreflight(ctx);
                return;
            }

            request.parse(fhr, decoder);
            if (log.isDebugEnabled()) {
                log.debug("{}", request);
            }

            // 触发前置拦截器
            if (HandlerInterceptorMgr.getInstance().triggerPreHandle(request, response)) {
                log.debug("请求被拦截器阻止 | ID: {}, URI: {}", requestId, uri);
                return;
            }

            HandleHttpClientRequestMgr.HandleWrapper wrapper = HandleHttpClientRequestMgr.getInstance()
                    .getHandleWrapper(request.getUri());
            if (wrapper == null) {
                log.warn("未找到路由处理程序 | ID: {}, URI: {}", requestId, uri);
                response.writeAndFlush(NOT_FOUND, "API未实现");
                return;
            }

            if (!wrapper.isPublicIpAccess() && !IpUtils.isInnerIp(ip)) {
                log.warn("IP未授权访问 | ID: {}, URI: {}, IP: {}", requestId, uri, ip);
                response.writeAndFlush(UNAUTHORIZED, "未授权访问");
                return;
            }

            if (wrapper.getMethod() != com.wg.net.server.http.HttpMethod.ALL) {
                com.wg.net.server.http.HttpMethod requestMethod = com.wg.net.server.http.HttpMethod
                        .valueOf(method.name());
                if (wrapper.getMethod() != requestMethod) {
                    log.warn("方法不匹配 | ID: {}, URI: {}, 请求方法: {}, 支持方法: {}",
                            requestId, uri, method, wrapper.getMethod());
                    response.writeAndFlush(METHOD_NOT_ALLOWED, "方法不允许");
                    return;
                }
            }

            wrapper.handleRequest(request, response);
        } catch (Exception e) {
            log.error("请求处理异常 | ID: {}, URI: {}, 异常: {}", requestId, uri, e.getMessage(), e);
            response.writeAndFlush(BAD_REQUEST, "请求参数无效: " + e.getMessage());
        } finally {
            ReferenceCountUtil.release(fhr);
        }
    }

    /**
     * 处理跨域预检请求（OPTIONS）
     */
    private void handleCorsPreflight(ChannelHandlerContext ctx) {
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1, OK,
                Unpooled.copiedBuffer("OK", StandardCharsets.UTF_8));

        response.headers()
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, String.join(",", corsConfig.getAllowedOrigins()))
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, String.join(",", corsConfig.getAllowedMethods()))
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, String.join(",", corsConfig.getAllowedHeaders()))
                .set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, String.valueOf(corsConfig.isAllowCredentials()))
                .set(HttpHeaderNames.ACCESS_CONTROL_MAX_AGE, String.valueOf(corsConfig.getMaxAge()))
                .set(HttpHeaderNames.CONNECTION, KEEP_ALIVE);

        ctx.writeAndFlush(response);
    }
}